#region File Header
//
// Dispatcher.cs - Dispatcher Queue
//
// Copyright (C) Javier Valcarce. BSD License
#endregion

#region Using Statements
using System;
using System.Collections.Generic;
using System.Threading;
using System.Diagnostics;
#endregion


namespace Dff
{
    /// <summary>
    /// Delegate for a task which will be enqueued
    /// </summary>
    public delegate void DispatcherDelegate(object arg);

    /// <summary>
    /// Simple dispatcher. The task queue is synchronized.
    /// </summary>
    public class Dispatcher
    {
        struct Task
        {
            public DispatcherDelegate task;
            public object             xarg;
        }

        bool shutdownRequested = false;
        bool shutdownCompleted = false;
        
        Queue<Task>      queue;
        ManualResetEvent newtask;
        Thread           current;

        #region Constructors
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="threadName">Event Queue Processor Thread Name</param>
        /// <param name="background">Put the thread in background</param>
        public Dispatcher()
        {
            // the thread that created this instance of Dispatcher
            current = Thread.CurrentThread;
            queue   = new Queue<Task>();
            newtask = new ManualResetEvent(false);
        }      
        #endregion

        #region Public Methods
        /// <summary>
        /// Fires an event handler, corresponding to index hnd in the event handler's table, and
        /// doesn't wait for finalization (asynchrnous call). If ThreadGG1K is stopped, the events 
        /// are still queued and proceced later, when it starts again.
        /// </summary>
        /// <param name="task">Task delegate (called with null parameter)</param>
        public void BeginInvoke(DispatcherDelegate task)
        {
            BeginInvoke(task, null);
        }

        /// <summary>
        /// Fires an event handler, corresponding to index hnd in the event handler's table, and
        /// doesn't wait for finalization (asynchrnous call). If ThreadGG1K is stopped, the events 
        /// are still queued and proceced later, when it starts again.
        /// </summary>
        /// <param name="task">Task delegate</param>
        /// <param name="arg">Parameter</param>
        public void BeginInvoke(DispatcherDelegate task, object arg)
        {
            Task t;
            t.task = task;
            t.xarg = arg;

            lock (queue)
            {
                queue.Enqueue(t);
                newtask.Set();
            }
        }


        public void BeginInvokeShutdown()
        {
            shutdownRequested = true;
            BeginInvoke(null, null);
        }
        #endregion


        #region Properties
        /// <summary>
        /// Returns the Thread object corresponding to the main thread, i.e., the thread which 
        /// run the message loop (dispatcher)
        /// </summary>
        public Thread Thread
        {
            get
            {
                return current;
            }
        }

        public bool HasShutdownStarted
        {
            get
            {
                return shutdownRequested;
            }
        }

        public bool HasShutdownFinished
        {
            get
            {
                return shutdownCompleted;
            }
        }

        #endregion

        // Dispatcher 
        public void Run()
        {
            Task t;

            while (true)
            {
                newtask.WaitOne();

                //Debug.WriteLine(String.Format("New Task. Thread {0} ID = {1}", Thread.CurrentThread.Name, 
                   // Thread.CurrentThread.ManagedThreadId));

                if (shutdownRequested)
                {
                    break;
                }

                lock (queue)
                {
                    Debug.Assert(queue.Count > 0);
                    t = queue.Dequeue();
                }

                // EXECUTE
                t.task(t.xarg);

                lock (queue)
                {
                    if (queue.Count == 0)
                    {
                        newtask.Reset();
                    }
                }
            }

            shutdownCompleted = true;
        }
    }
}